package breadbox.client.operations;

import java.io.IOException;
import java.rmi.AlreadyBoundException;
import java.rmi.ConnectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import breadbox.client.files.Chunk;
import breadbox.client.files.FileChunk;
import breadbox.client.files.Unchunker;
import breadbox.dataManagement.RMIMessage;
import breadbox.pastry.Message;

/**
 * Client Implementation of RMIMessage
 * Delivers messages from dataManagement to client
 * @author Forrest
 *
 */

public class RMIMessageReceiver extends UnicastRemoteObject implements RMIMessage  {
	private static final long serialVersionUID = 5317980828218339922L;
	private List<Chunk> chunks;
	private final int BREADBOX_SERVER_PORT = 1112;
	
	/**
	 * Constructs a new RMIMEssageReceiver
	 */
	public RMIMessageReceiver() throws RemoteException {
		super();
		chunks = Collections.synchronizedList(new ArrayList<Chunk>());
	}
	
	/**
	 * Sets up the RMI connection to the server
	 * @return RMIMessage server
	 * @throws RemoteException
	 */
	public RMIMessage getRMIServer() throws RemoteException {
		Registry registry = LocateRegistry.getRegistry(BREADBOX_SERVER_PORT);
		RMIMessage server = null;
		try {
			server = (RMIMessage)registry.lookup("pastryServer");
		} catch (NotBoundException e) {
			System.err.println("Registry error: Registry not bound");
			System.exit(1);
		} catch (ConnectException e) {
			System.err.println("Registry error: Connection failed");
			System.exit(1);
		}
		
		// return the reference to the server for use by other client classes
		return server;
	}
	
	/**
	 * Sets up the local RMI server and sends an
	 * initialization message to the breadbox server
	 * @throws RemoteException 
	 */
	public void setupRMI(RMIMessage server) throws RemoteException {
		Registry registry = LocateRegistry.getRegistry(BREADBOX_SERVER_PORT);
		try {
			registry.bind("RMIMessageReceiver", this);
		} catch (AlreadyBoundException e) {
			registry.rebind("RMIMessageReceiver", this);
		}
		
		Message m = new Message(0, OperationType.INITIALIZE.toString(), null);
		server.sendMessage(m);
	}
	
	/**
	 * Checks if all chunks for a file have been received
	 * @param t Total number of chunks
	 * @return boolean TRUE if all chunks are in the list, else FALSE
	 */
	private boolean haveAllChunks(int t) {
		return (chunks.size() == t);
	}
	
	/**
	 * Returns the current list of Chunks
	 * @return List<Chunk> list of Chunks
	 */
	public List<Chunk> getAllChunks() {
		return this.chunks;
	}
	
	/**
	 * Sends a message into this system
	 */
	public void sendMessage(Message m) throws RemoteException {
		OperationType op = OperationType.valueOf(m.key);
		switch (op) {
		case GET_ERROR:		// error message response to GET
			System.err.println("Error: An unknown GET error occurred");
			break;
		case GET_RESPONSE:	// non-error response to GET
			handleGetResponse(m);
			break;
		default:			// unsupported message type
			System.err.println("Error: Operation Not Supported: " + m.key);
		}
	}
	
	/**
	 * Process a GET_RESPONSE message
	 * @param m
	 * @throws RemoteException
	 */
	private void handleGetResponse(Message m) throws RemoteException {
		// create the Chunk from the data in the message
		Chunk k = null;
		try {
			k = new FileChunk(m.data);
		} catch (IOException e) {
			System.err.println("Get error: Unable to construct data from message");
		} catch (ClassNotFoundException e) {
			System.err.println("Get error: Cannot create file chunk from message");
		}

		chunks.add(k.getIndex(), k);
		
		// when all chunks for the file have been transferred, send them to the Unchunker
		if (haveAllChunks(k.getTotal())) {
			Unchunker u = new Unchunker(chunks, true);
			int status = u.unchunk();
			if (status == 0) {
				System.out.println("Get operation successful!");
				Registry registry = LocateRegistry.getRegistry(BREADBOX_SERVER_PORT);
				try {
					registry.unbind("RMIMessageReceiver");
				} catch (NotBoundException e) {
					e.printStackTrace();
				}
				System.exit(status);
			}
		}
	}
}
